/*******************************************************************************
 * Copyright (c) 2007 University of Oslo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University of Oslo - initial API and implementation
 *******************************************************************************/

package com.cea.papyrus.diagram.sequence.sedi.edit;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.gef.ConnectionEditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.NodeEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.requests.DropRequest;
import org.eclipse.ui.views.properties.IPropertySource;
import org.eclipse.uml2.uml.NamedElement;

import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.ui.properties.advanced.EObjectPropertySource;
import com.cea.papyrus.diagram.sequence.sedi.figures.AnchoringFigure;
import com.cea.papyrus.diagram.sequence.sedi.figures.SeDiAnchor;
import com.cea.papyrus.diagram.sequence.sedi.model.command.MessageEndPosition;
import com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil;
import com.cea.papyrus.diagraminterchange2.di2.Di2Package;
import com.cea.papyrus.diagraminterchange2.di2.GraphConnector;
import com.cea.papyrus.diagraminterchange2.di2.GraphEdge;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;

/**
 * TODO Rename class, write documentation
 * 
 * @author Andreas Limyr
 * @author frankd@ifi.uio.no
 */
public abstract class SeDiSubpartEditPart<T extends NamedElement> extends
        AbstractGraphicalEditPart<T> implements NodeEditPart {

    private IPropertySource propertySource;

    public SeDiSubpartEditPart(GraphElement model, ModelManager modelManager) {
        super(model, modelManager);
    }

    // --- Activation ---

    @Override
    protected void graphicalModelChanged(Notification evt) {
        switch (evt.getFeatureID(Di2Package.class)) {
        case Di2Package.GRAPH_ELEMENT__ANCHORAGE:
            refreshSourceConnections();
            refreshTargetConnections();
            break;
        default:
        }
        int type = evt.getEventType();
        if ((type == Notification.SET) || (type == Notification.ADD)) {
            if (((GraphElement) this.getModel()).getContainer() != null) {
                this.getModelManager().modifGraphElement((GraphNode) this.getModel());
                this.refresh();
            }
        }
        super.graphicalModelChanged(evt);
    }

    // --- Model communication ---

    @SuppressWarnings("unchecked")
    protected List getModelSourceConnections() {
        // TODO Replace the next one by a view list (no list creation)
        List<GraphEdge> res = new ArrayList<GraphEdge>();
        Iterator<GraphConnector> anchors = ((GraphNode) getModel()).getAnchorage().iterator();
        while (anchors.hasNext()) {
            GraphConnector anchor = anchors.next();
            Iterator<GraphEdge> edges = anchor.getGraphEdge().iterator();
            while (edges.hasNext()) {
                GraphEdge edge = edges.next();
                // Check if this is a source anchor.
                if (edge.getAnchor().get(0) == anchor)
                    res.add(edge);
            }
        }
        return res;
    }

    @SuppressWarnings("unchecked")
    protected List getModelTargetConnections() {
        // TODO Replace the next one by a view list (no list creation)
        List<GraphEdge> res = new ArrayList<GraphEdge>();
        Iterator<GraphConnector> anchors = ((GraphNode) getModel()).getAnchorage().iterator();
        while (anchors.hasNext()) {
            GraphConnector anchor = anchors.next();
            Iterator<GraphEdge> edges = anchor.getGraphEdge().iterator();
            while (edges.hasNext()) {
                GraphEdge edge = edges.next();
                // Check if this is a source anchor.
                if (edge.getAnchor().get(1) == anchor)
                    res.add(edge);
            }
        }
        return res;
    }

    @SuppressWarnings("unchecked")
    public ConnectionAnchor getSourceConnectionAnchor(ConnectionEditPart conn) {
        GraphEdge msg = (GraphEdge) conn.getModel();
        String sourceRef = ModelUtil.getSourceRef(msg);
        if (sourceRef == null) {
            AnchoringFigure anchoringFigure = (AnchoringFigure) getFigure();
            this.refreshVisualsBounds();
            Point position = msg.getAnchor().get(0).getPosition().getCopy();
            position = position.translate(getFigure().getBounds().getLocation());
            getFigure().translateToAbsolute(position);
            boolean right =
                    msg.getAnchor().get(0).getPosition().x <= msg.getAnchor().get(1).getPosition().x;
            SeDiAnchor anchor = (SeDiAnchor) anchoringFigure.getAnchorAt(position, right);
            ModelUtil.setSourceRef(msg, anchor.getTag());
        }
        return getConnectionAnchor(ModelUtil.getSourceRef(msg));
    }

    @SuppressWarnings("unchecked")
    public ConnectionAnchor getTargetConnectionAnchor(ConnectionEditPart conn) {
        GraphEdge msg = (GraphEdge) conn.getModel();
        String targetRef = ModelUtil.getTargetRef(msg);
        if (targetRef == null) {
            this.refreshVisualsBounds();
            Point position = msg.getAnchor().get(1).getPosition().getCopy();
            position = position.translate(getFigure().getBounds().getLocation());
            getFigure().translateToAbsolute(position);
            boolean right =
                    msg.getAnchor().get(1).getPosition().x <= msg.getAnchor().get(0).getPosition().x;
            SeDiAnchor anchor =
                    (SeDiAnchor) ((AnchoringFigure) getFigure()).getAnchorAt(position, right);
            ModelUtil.setTargetRef(msg, anchor.getTag());
        }
        return getConnectionAnchor(ModelUtil.getTargetRef(msg));
    }

    public ConnectionAnchor getSourceConnectionAnchor(Request request) {
        Point pt = new Point(((DropRequest) request).getLocation());
        ((GraphicalEditPart) getRoot()).getFigure().translateToAbsolute(pt);
        return ((AnchoringFigure) getFigure()).getAnchorAt(pt);
    }

    public ConnectionAnchor getTargetConnectionAnchor(Request request) {
        Point pt = new Point(((DropRequest) request).getLocation());
        ((GraphicalEditPart) getRoot()).getFigure().translateToAbsolute(pt);
        return ((AnchoringFigure) getFigure()).getAnchorAt(pt);
    }

    /**
     * Returns the ConnectionAnchor having the given tag.
     * 
     * @param ref
     *            The tag that identifies the anchor. Must not be null.
     * @return the ConnectionAnchor. The method always returns an Anchor; it is the
     *         responsibility of the caller to ask for an anchor that is valid - that is,
     *         an anchor that is (will be) laid out by the anchoring figure.
     */
    public ConnectionAnchor getConnectionAnchor(final String ref) {
        return ((AnchoringFigure) getFigure()).getAnchor(ref);
    }

    /**
     * Finds the position of the given anchor on the element represented by this editpart.
     * The exact algorithm depends on the type of the element. For more information, see
     * {@link com.cea.papyrus.diagram.sequence.sedi.model.command.MessageEndPosition} and
     * its subclasses.
     * 
     * @param anchor
     *            The anchor whose position on the lifeline is desired.
     * @param send
     *            set to <code>true</code> if the anchor is to be considered as the send
     *            end of a message, or <code>false</code> if the anchor should be
     *            considered the receive end of a message.
     * @return an object that specifies the exact model position of a message sent or
     *         received at the anchor. TODO Refactor - the argument 'owner' should either
     *         be removed and calculated by the method; or the entire method can be moved
     *         to a command (and removed from the class).
     */
    @SuppressWarnings("unchecked")
    public MessageEndPosition getMessagePosition(final ConnectionAnchor anchor, boolean send,
            final NamedElement owner) {
        // for the time being, order is irrelevant so let's just place
        // everything where-ever it ends up
        return new MessageEndPosition(owner);
    }

    /**
     * Gets the adapter.
     * 
     * @param key
     *            the key
     * @return the adapter
     */
    @Override
    public Object getAdapter(Class key) {
        if (IPropertySource.class == key) {
            return this.getPropertySource();
        }

        return super.getAdapter(key);
    }

    /**
     * Gets the property source.
     * 
     * @return the property source
     */
    protected IPropertySource getPropertySource() {
        if (this.propertySource == null) {
            this.propertySource = new EObjectPropertySource(this.getUmlElement());
        }
        return this.propertySource;
    }

    // public final AnchorMngr getAnchorMngr() {
    // if (this.anchorMngr == null) {
    // this.createAnchorMngr();
    // }
    // return this.anchorMngr;
    // }
    //
    // public void createAnchorMngr() {
    // this.anchorMngr = new RectangleAnchorMngr(this);
    // }
}
